/*
 * MIT License
 *
 * Copyright (c) 2019 Mr.css
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package com.wendy.common.core.utils.reflect;

import com.esotericsoftware.reflectasm.MethodAccess;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.Function;

/**
 * Java反射改进 —— Field注入
 * <p>
 * 对标的产品：<code>com.esotericsoftware.reflectasm.FieldAccess</code><br/>
 * <p>
 * 主要是在使用方式上进行了优化：
 * 业务场景下，字段名使用频次更多；
 * 而存取值的时候，使用 setter/getter 才更加合理。
 * <p>
 * 这个类解决了这个问题，看起来是通过 Field 进行反射操作，实际上使用的是 setter/getter，使用起来会更加舒适一些。
 * <p>
 * 1、字段强制注入，不符合开发规范，在新的版本中，其实调用的仍然是 getter 和 setter；
 * 2、注入字段值的时候，不需要关注 getter/setter 的函数名，直接用字段名注入即可；
 * 3、其中，names 采用的是自然排序，字段检索时，采用折半查找的方式，字段较多时有优势；
 * 4、字段解析的时候，已经过滤掉了所有静态字段。
 *
 * <p> 2020-05-14 21:21 bate 1.0.0
 *
 * @author Mr.css
 * @version 2022-10-22 21:21 v1.0.0
 */
public class FieldAccess {
    private final Logger logger = LoggerFactory.getLogger(FieldAccess.class);
    /**
     * 缓存已经解析过的JavaBean
     */
    private static final Map<Class<?>, FieldAccess> cache = new HashMap<>();
    /**
     * Java类型
     */
    private final Class<?> classType;
    /**
     * 字段名数组
     */
    private final String[] names;
    /**
     * 字段数组
     */
    private final ObjectField[] fields;

    /**
     * 创建一个新的FieldAccess实例
     *
     * @param clazz class
     * @return FieldAccess
     */
    public static FieldAccess create( Class<?> clazz) {
        FieldAccess access = cache.get(clazz);
        if (access == null) {
            access = new FieldAccess(clazz);
            cache.put(clazz, access);
        }
        return access;
    }

    /**
     * -
     *
     * @param clazz -
     */
    private FieldAccess( Class<?> clazz) {
        // 对于基础类型数据不做解析，因为会引发很多未知的错误，这里手动抛出一个异常
        if (!ObjectType.isObject(clazz)) {
            throw new IllegalArgumentException("Is not a java bean: " + clazz);
        }

        // 字节码解析工具
        MethodAccess access = MethodAccess.get(clazz);
        this.classType = clazz;

        // 扫描所有字段
        List<ObjectField> objectFields = this.analyzerField();
        this.names = new String[objectFields.size()];

        // 获取所有字段名，重新排序，方便进行折半检索
        int idx = 0;
        for (ObjectField objectField : objectFields) {
            names[idx] = objectField.getName();
            objectField.setMethodAccess(access);
            idx++;
        }
        Arrays.sort(this.names);

        // 对字段进行排序
        List<ObjectField> list = new ArrayList<>();
        for (String name : this.names) {
            for (ObjectField field : objectFields) {
                if (name.equals(field.getName())) {
                    list.add(field);
                    break;
                }
            }
        }
        // 经过排序的 字段数组
        this.fields = list.toArray(new ObjectField[]{});
    }

    /**
     * 扫描所有的字段，直到检索到Object位置
     *
     * @return 字段集合
     */
    private List<ObjectField> analyzerField() {
        List<ObjectField> ret = new ArrayList<>();
        Class<?> cla = this.classType;
        while (cla != Object.class) {
            if (cla == null) {
                // 如果字段是一个接口，那么它是没有父类的
                break;
            } else {
                Field[] fields = cla.getDeclaredFields();
                for (Field field : fields) {
                    if (!Modifier.isStatic(field.getModifiers())) {
                        ret.add(this.convertToObjectField(cla, field));
                    }
                }
                cla = cla.getSuperclass();
            }
        }
        return ret;
    }

    /**
     * 解析字段，包括对象的setter/getter，此过程比较耗时
     *
     * @param clazz 字段所属的对象
     * @param field 字段对象
     * @return -
     */
    private ObjectField convertToObjectField(Class<?> clazz, Field field) {
        String fieldName = field.getName();

        ObjectField objectField = new ObjectField();
        objectField.setName(fieldName);
        objectField.setField(field);
        objectField.setClassType(clazz);

        // field - type
        Class<?> type = field.getType();
        objectField.setFieldType(type);
        objectField.setObjectType(ObjectType.of(type));

        // getter
        Method getter = MethodUtils.getGetter(clazz, fieldName);
        if (getter == null) {
            // getter and field not match, had better follow norms.
            // allow this to happen, maybe possible require in special business.
            logger.debug("getter not found: " + clazz + "." + fieldName);
        } else {
            objectField.setGetter(getter.getName());
        }

        // setter
        Method setter = MethodUtils.getSetter(clazz, fieldName, type);
        if (setter == null) {
            // setter and field do not match, had better follow norms.
            // allow this to happen, maybe possible require in special business.
            logger.debug("setter not found: " + clazz + "." + fieldName);
        } else {
            objectField.setSetter(setter.getName());
        }
        return objectField;
    }

    /**
     * 获取字段对象
     *
     * @param name 字段名称
     * @return Field
     */
    public ObjectField getField(String name) {
        int idx = Arrays.binarySearch(names, name);
        return idx >= 0 ? fields[idx] : null;
    }

    /**
     * 设值
     *
     * @param instance  对象实例
     * @param fieldName 字段名
     * @param value     值
     */
    public void setValue(Object instance, String fieldName, Object value) {
        this.getField(fieldName).setValue(instance, value);
    }

    /**
     * 设值，采用接口回调的方式，允许在设值的时候，做一些额外的逻辑运算，减少重复的数组遍历工作
     *
     * @param instance 对象实例
     * @param accept   接口回调
     */
    public void setValue(Object instance, Function<String, Object> accept) {
        for (ObjectField objectField : fields) {
            objectField.setValue(instance, accept.apply(objectField.getName()));
        }
    }

    /**
     * 设值
     *
     * @param instance 对象实例
     * @param idx      字段序号
     * @param value    值
     */
    public void setValue(Object instance, int idx, Object value) {
        fields[idx].setValue(instance, value);
    }


    /**
     * 取值
     *
     * @param instance  对象实例
     * @param fieldName 字段名
     * @return 值
     */
    public Object getValue(Object instance, String fieldName) {
        return this.getField(fieldName).getValue(instance);
    }

    /**
     * 取值，这里可能会因为数据越界而抛出异常
     *
     * @param instance 对象实例
     * @param idx      字段序号
     * @return 值
     */
    public Object getValue(Object instance, int idx) {
        return fields[idx].getValue(instance);
    }

    /**
     * 获取字段备份
     *
     * @return 字段数组
     */
    public ObjectField[] getFields() {
        return fields != null ? Arrays.copyOf(fields, fields.length) : null;
    }

    /**
     * 获取字段名备份
     *
     * @return 字段名数组
     */
    public String[] getNames() {
        return names != null ? Arrays.copyOf(names, names.length) : null;
    }


    /**
     * 获取字段对象，数据越界不会抛出异常，只会返回null
     *
     * @param idx 字段序号
     * @return Field
     */
    public ObjectField getField(int idx) {
        return (idx > 0 && idx < fields.length) ? fields[idx] : null;
    }


    /**
     * 获取字段对象，数据越界不会抛出异常，只会返回null
     *
     * @param idx 字段序号
     * @return String
     */
    public String getFieldName(int idx) {
        return (idx >= 0 && idx < names.length) ? names[idx] : null;
    }

    /**
     * 获取字段序号
     *
     * @param name 字段名称
     * @return int
     */
    public int indexOfField(String name) {
        return Arrays.binarySearch(names, name);
    }


    /**
     * 获取Java类型
     *
     * @return Class
     */
    public Class<?> getClassType() {
        return classType;
    }

    /**
     * 创建一个对象实例
     *
     * @return instance
     */
    public <T> T newInstance() {
        try {
            return (T) classType.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException("create new instance failed：", e);
        }
    }
}
